package edu.unlv.cs.ladders.web;

import java.util.List;

import javax.validation.Valid;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import edu.unlv.cs.ladders.entities.Gender;
import edu.unlv.cs.ladders.entities.Participant;
import edu.unlv.cs.ladders.entities.Team;
import edu.unlv.cs.ladders.entities.UserRole;
import edu.unlv.cs.ladders.security.NewlyRegisteredAuthentication;
import edu.unlv.cs.ladders.services.PermissionService;

import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;

@RequestMapping("/participant/**")
@Controller
public class ParticipantController {
	private static final Logger LOG = Logger.getLogger(ParticipantController.class);
	@RequestMapping(value = "/participant", method = RequestMethod.POST)
	public String create(@Valid Participant participant, BindingResult result,
			ModelMap modelMap) {
		LOG.info("creating new participant.");
		if (participant == null)
			throw new IllegalArgumentException("A participant is required");
		if (result.hasErrors()) {
			modelMap.addAttribute("participant", participant);
	        modelMap.addAttribute("gender_enum", Gender.class.getEnumConstants());
			return "participant/create";
		}
		participant.persist();
		participant.flush();
		participant.addRole(UserRole.getParticipantRole());
		this.authenticateParticipant(participant);
		this.getPermissionService().setDefaultPermissions(participant);
		return "redirect:/participant/" + participant.getId();
	}

	private void authenticateParticipant(final UserDetails user) {
		try {
			// Must be called from request filtered by Spring Security,
			// otherwise SecurityContextHolder is not updated
			Authentication token = new NewlyRegisteredAuthentication(user);
			SecurityContextHolder.getContext()
					.setAuthentication(token);
		} catch (Exception e) {
			LOG.warn("Error logging in.",e);
			SecurityContextHolder.getContext().setAuthentication(null);
		}
	}

	@PreAuthorize("hasPermission(#participant, 'write') or hasRole('ROLE_COORDINATOR')")
	@RequestMapping(method = RequestMethod.PUT)
	public String update(@Valid Participant participant, BindingResult result,
			ModelMap modelMap) {
		LOG.info("!!!!!!!!!!!!!!!!!!!!!!!!!invoking participant controller update.");
		if (participant == null)
			throw new IllegalArgumentException("A participant is required");
		if (result.hasErrors()) {
			modelMap.addAttribute("participant", participant);
			modelMap.addAttribute("gender_enum", Gender.class
					.getEnumConstants());
			modelMap.addAttribute("teams", Team.findAllTeams());
			modelMap.addAttribute("userroles", UserRole.findAllUserRoles());
			return "participant/update";
		}
		String username = participant.getUsername();
		String name = participant.getName();
		String phone = participant.getPhone();
		participant = Participant.findParticipant(participant.getId());
		participant.setUsername(username);
		participant.setName(name);
		participant.setPhone(phone);
		participant.merge();
		return "redirect:/participant/" + participant.getId();
	}

	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/participant/{id}", method = RequestMethod.DELETE)
	public String delete(@PathVariable("id") Long id,
			@RequestParam(value = "page", required = false) Integer page,
			@RequestParam(value = "size", required = false) Integer size) {
		if (id == null)
			throw new IllegalArgumentException("An Identifier is required");
		Participant participant = Participant.findParticipant(id);
		participant.setEnabled(false);
		participant.merge();
		return "redirect:/participant?page="
				+ ((page == null) ? "1" : page.toString()) + "&size="
				+ ((size == null) ? "10" : size.toString());
	}

	
    @RequestMapping(value = "/participant/form", method = RequestMethod.GET)
    public String createForm(ModelMap modelMap) {
        modelMap.addAttribute("participant", new Participant());
        modelMap.addAttribute("gender_enum", Gender.class.getEnumConstants());
        return "participant/create";
    }
    
    @RequestMapping(value = "/participant/{id}", method = RequestMethod.GET)
    public String show(@PathVariable("id") Long id, ModelMap modelMap) {
        if (id == null) throw new IllegalArgumentException("An Identifier is required");
        modelMap.addAttribute("participant", Participant.findParticipant(id));
        return "participant/show";
    }

    @SuppressWarnings({ "unchecked" })
    @RequestMapping(value = "/participant", method = RequestMethod.GET)
    public String list(@RequestParam(value = "page", required = false) Integer page, @RequestParam(value = "size", required = false) Integer size, ModelMap modelMap) {
        if (page != null || size != null) {
			List<Participant> participants = Participant.findParticipantsByEnabled(true).getResultList();
            int sizeNo = size == null ? 10 : size.intValue();
            
            int startIndex = page == null ? 0 : (page.intValue() - 1) * sizeNo;
            startIndex = Math.min(startIndex, participants.size());
            startIndex = Math.max(0, startIndex);
            
            int endIndex = startIndex + sizeNo;
            endIndex = Math.min(endIndex, participants.size());
            endIndex = Math.max(0, endIndex);
            
            modelMap.addAttribute("participants", participants.subList(startIndex,endIndex));
            float nrOfPages = (float) Participant.countParticipants() / sizeNo;
            modelMap.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));
        } else {
            modelMap.addAttribute("participants", Participant.findParticipantsByEnabled(false).getResultList());
        }
        return "participant/list";
    }
    
    @PreAuthorize("hasPermission(#id,'edu.unlv.cs.ladders.entities.Participant','write') or hasRole('ROLE_COORDINATOR')")
    @RequestMapping(value = "/participant/{id}/form", method = RequestMethod.GET)
    public String updateForm(@PathVariable("id") Long id, ModelMap modelMap) {
        if (id == null) throw new IllegalArgumentException("An Identifier is required");
        modelMap.addAttribute("participant", Participant.findParticipant(id));
        modelMap.addAttribute("gender_enum", Gender.class.getEnumConstants());
        modelMap.addAttribute("teams", Team.findAllTeams());
        modelMap.addAttribute("userroles", UserRole.findAllUserRoles());
        return "participant/update";
    }
    
    @RequestMapping(value = "find/ByUsernameEquals/form", method = RequestMethod.GET)
    public String findParticipantsByUsernameEqualsForm(ModelMap modelMap) {
        return "participant/findParticipantsByUsernameEquals";
    }
    
    @RequestMapping(value = "find/ByUsernameEquals", method = RequestMethod.GET)
    public String findParticipantsByUsernameEquals(@RequestParam("username") String username, ModelMap modelMap) {
        if (username == null || username.length() == 0) throw new IllegalArgumentException("A Username is required.");
        modelMap.addAttribute("participants", Participant.findParticipantsByUsernameEquals(username).getResultList());
        return "participant/list";
    }
	private PermissionService permissionService;

	@Autowired
	public void setPermissionService(PermissionService permissionService) {
		this.permissionService = permissionService;
	}

	public PermissionService getPermissionService() {
		return permissionService;
	}

}
